package com.fusion.core.comm;

import java.io.IOException;

import com.fusion.Version;
import com.fusion.core.interactions.FriendAlreadyInListUserInteraction;
import com.fusion.core.node.Friend;
import com.fusion.core.node.MyNode;
import com.fusion.core.settings.Server;

/**
 * This connection swings both ways - it's used by invitor and invited
 *
 * @author SwethaShivakumar 
 * Project: fusion
 * Date: Mar 6, 2009
 * URL: http://fusionwiki.co.cc
 * Mail Me At: shiva@fusionproject.co.cc
 */
public class InvitationConnection extends AuthenticatedConnection {
    public static final int CONNECTION_ID=4;
    private int passkey;
    private Friend middleman;

    private Runnable connectionFailedEvent;


    public InvitationConnection(NetworkManager netMan, Direction direction, int passkey, Friend middleman) {
        super(netMan, direction);
        this.passkey = passkey;
        this.middleman = middleman;
    }

    public InvitationConnection(NetworkManager netMan, Direction direction, Object key,  int passkey, Integer middlemanGuid) {
        super(netMan, direction, key);
        this.passkey = passkey;
        if (middlemanGuid != null && middlemanGuid != 0) {
            middleman = core.getFriendManager().getFriend(middlemanGuid);
            if (middleman == null) if(T.t)T.error("Could not find middleman: "+middlemanGuid);
        }

        if(T.t)T.ass(direction == Direction.IN, "Only supports incoming connections!");
        sendMyInfoWrapped();
    }

    public void sendConnectionIdentifier() throws IOException {
        if(T.t)T.trace("InvitationConnection succeded - remove connection error event");
        connectionFailedEvent = null;

        if(T.t)T.trace("Sending special authentication for InvitationConnection");
        Packet p = netMan.createPacketForSend();
        p.writeInt(Version.PROTOCOL_VERSION);
        p.writeByte((byte)getConnectionIdForRemote());
        p.writeInt(passkey);
        send(p);

        if(T.t)T.ass(direction == Direction.OUT, "Only supports outgoing connections!");
        sendMyInfoWrapped();
    }

    private void sendMyInfoWrapped() {
        try {
            sendMyInfo();
        } catch(IOException e) {
            core.reportError(e, this);
        }
    }
    private void sendMyInfo() throws IOException {
        if(T.t)T.info("Sending my info because remote had correct invitation passkey");
        Packet p = netMan.createPacketForSend();
        p.writeInt(core.getFriendManager().getMyGUID());

        Server server = core.getSettings().getServer();
        MyNode me = core.getFriendManager().getMe();

        if (server.getHostname() != null) {
            p.writeUTF(server.getHostname());
        } else {
            p.writeUTF(me.getExternalIp(core));
        }

        p.writeInt(server.getPort());

        p.writeUTF(me.getNickname());

        send(p);
    }

    private boolean friendInfoReceived = false;
    public void packetReceived(Packet p) throws IOException {
        if (friendInfoReceived) {
            if(T.t)T.info("InvitationConnection complete - both side has agreed on closing connection - so lets close it");
            close();
        } else {
            if(T.t)T.info("Received info of new friend!");
            friendInfoReceived = true;
            int guid = p.readInt();
            String host = p.readUTF();
            int port = p.readInt();
            String name = p.readUTF();

            com.fusion.core.settings.Friend newFriend = new com.fusion.core.settings.Friend(name, host, guid, port, middleman == null ? null : middleman.getGuid());
            for(com.fusion.core.settings.Friend f : core.getSettings().getFriendlist()) if (f.getGuid() == guid) {
                //friend already my friend, update ip number
                com.fusion.core.node.Friend friend = core.getFriendManager().getFriend(f.getGuid());
                if (friend != null && !friend.isConnected()) {
                    if (friend.updateLastKnownHostInfo(host, port)) {
                        core.getFriendManager().getFriendConnector().queHighPriorityConnectTo(friend);
                    }
                }
                core.queNeedsUserInteraction(new FriendAlreadyInListUserInteraction(newFriend.getGuid()));
                return;
            }

            //new friend connected!
            core.getSettings().getFriendlist().add(newFriend);
            try {
                core.saveSettings();
                Friend f = core.getFriendManager().addFriend(newFriend, true, middleman != null);
                core.getFriendManager().getFriendConnector().queHighPriorityConnectTo(f, (int)(Math.random()*1000+1000));

            } catch(Exception e) {
                core.reportError(e, this);
            }

            send(netMan.createPacketForSend()); //send an empty packet to trigger packetReceived on other end again.
        }
    }

    protected int getConnectionId() {
        return CONNECTION_ID;
    }

    public void signalConnectionAttemtError() {
        super.signalConnectionAttemtError();
        if (connectionFailedEvent != null) {
            connectionFailedEvent.run();
            connectionFailedEvent = null;
        }
    }

    public void setConnectionFailedEvent(Runnable connectionFailedEvent) {
        this.connectionFailedEvent = connectionFailedEvent;
    }
}
